package soccerBet.project.main;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Semaphore;

import org.apache.axis2.databinding.types.xsd.DateTime;
import org.kie.api.KieServices;
import org.kie.api.event.rule.DebugAgendaEventListener;
import org.kie.api.event.rule.DebugRuleRuntimeEventListener;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

import soccerBet.project.Calculation.AvaliationFunction;
import soccerBet.project.Calculation.Team;
import soccerBet.project.Transaction.BetsTM;
import soccerBet.project.betfair.global.BFGlobalServiceStub.BFEvent;
import soccerBet.project.betfair.global.BFGlobalServiceStub.GetEventsResp;
import soccerBet.project.betfair.global.BFGlobalServiceStub.MarketSummary;
import soccerBet.project.betfair.handler.ExchangeAPI;
import soccerBet.project.betfair.handler.GlobalAPI;
import soccerBet.project.betfair.handler.ExchangeAPI.Exchange;
import soccerBet.project.betfair.util.APIContext;
import soccerBet.project.betfair.util.InflatedMarketPrices;
import soccerBet.project.betfair.util.InflatedMarketPrices.InflatedPrice;
import soccerBet.project.betfair.util.InflatedMarketPrices.InflatedRunner;
import soccerBet.project.mail.SendMailTLS;
import soccerBet.project.objects.Classificacao;
import soccerBet.project.objects.Jogo;
import soccerBet.project.objects.Liga;
import soccerBet.project.objects.MapeamentoMercados;
import soccerBet.project.objects.Mercados;
import soccerBet.project.objects.Precos;
import soccerBet.project.ruleEngine.ApostaRule;
import soccerBet.project.ruleEngine.EngineStart;
import soccerBet.project.ruleEngine.TempoJogo;
import soccerBet.project.services.BetService;
import soccerBet.project.services.EmailService;
import soccerBet.project.services.InputValuesService;
import soccerBet.project.services.LigaService;
import soccerBet.project.services.MercadoService;
import soccerBet.project.utils.Display;
import soccerBet.project.utils.Prop;
import soccerBet.project.utils.Utils;

import soccerBet.project.betfair.exchange.BFExchangeServiceStub.BetCategoryTypeEnum;
import soccerBet.project.betfair.exchange.BFExchangeServiceStub.BetPersistenceTypeEnum;
import soccerBet.project.betfair.exchange.BFExchangeServiceStub.BetTypeEnum;
import soccerBet.project.betfair.exchange.BFExchangeServiceStub.PlaceBets;
import soccerBet.project.betfair.exchange.BFExchangeServiceStub.PlaceBetsResult;
import soccerBet.project.contracts.RunnerContract;
import soccerBet.project.dao.ApostasDAO;
import soccerBet.project.dao.ClassificacaoDAO;
import soccerBet.project.dao.JogoDAO;
import soccerBet.project.dao.LigaDAO;
import soccerBet.project.dao.MapeamentoMercadosDAO;
import soccerBet.project.dao.MercadoDAO;
import soccerBet.project.dao.PrecosDAO;
import soccerBet.project.dao.RunnerDAO;
import soccerBet.project.funds.Funds;

public class ServiceManager extends Thread {

	// The session token
	public static APIContext apiContext = new APIContext();	
	private Jogo jogo;
	public MercadoService mercService = new MercadoService();
	public LigaService ligaService = new LigaService();
	public BetsTM betsTM = new BetsTM();
	public Semaphore semaphore;
	public long tempoFinal = 0;
	
	public ServiceManager(){
		
	}
	
	public ServiceManager(Jogo jogo,Semaphore semaphore){
		this.jogo=jogo;
		this.semaphore=semaphore;
		this.tempoFinal=(this.jogo.getData().getTime()+(110*60*1000));
	}
	
	
	public void run() {

		GetEventsResp resp = null;
		int[] marketId = new int[1]; 
		int[] eventId = new int[1]; 
		eventId[0] = 0;
      
		List<Mercados> mercados = mercService.getMercadosActivos();
		Liga liga = ligaService.getLigaByJogo(jogo.getIdJogo());
		List<Mercados> removedMarkets = null;
		List<Mercados> addedMarkets = null;
		
		while(apiContext.getToken()==null){
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		Display.println("Efectua calculos antes do motor de regras...");
		InputValuesService ivs = new InputValuesService(jogo);
		Display.println("Calculos efectuados...");
		
  

		
		while(new Date().getTime() < this.tempoFinal){
			
			try {
				removedMarkets = new ArrayList<Mercados>();
				addedMarkets = new ArrayList<Mercados>();
				MarketSummary[] markets = null;
		        if(eventId[0] == 0){
		
		              BFEvent[] eventDates = getEvents(resp, liga.getIdLigaBetfair());
		  
		              BFEvent[] gamesEvents = getEvents(resp,eventDates,jogo.getDate());
		  
		              markets = getMarkets(resp,gamesEvents,Utils.buildGameRegex(this.jogo),this.jogo,eventId);
		            
		        }
		        else{
		               markets = getMarkets(resp,eventId);
		        }

				for(Mercados mercado : mercados){

					InflatedMarketPrices prices = getPrices(marketId,markets,mercado.getDesignacao(),semaphore);

					if(!prices.getMarketStatus().equals("CLOSED")){

						List<RunnerContract> runners = getRunners(prices);

						List<Precos> precosList = betsTM.makeTransaction(marketId, runners,this.jogo, mercado);

						callRulesEngine(this.jogo,precosList,marketId,ivs);
					}else{

						betsTM.closedMarket(marketId,this.jogo, mercado);
						removedMarkets.add(mercado);
						addedMarkets =(verificaSucessao(mercado,addedMarkets));
					}
					
				}
				
				filterMarkets(mercados,removedMarkets,addedMarkets);
				
				Display.println("-------------------sleep 10s------------------");
				Thread.sleep(10000);
			
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		this.interrupt();
		
	}
	
	private List<Mercados>  verificaSucessao(Mercados mercado,
			List<Mercados> addedMarkets) {
		// TODO Auto-generated method stub

		if(mercado.getSucessao()!=0){
			addedMarkets.add(mercService.getMercado(mercado.getSucessao()));
		}
		
		return addedMarkets;
	}

	private void filterMarkets(List<Mercados> mercadoObjList,
			List<Mercados> removedMarkets, List<Mercados> addedMarkets) {
		// TODO Auto-generated method stub
		if(removedMarkets.size()>0){
			mercadoObjList.removeAll(removedMarkets);
		}
		if(addedMarkets.size()>0){
			mercadoObjList.addAll(addedMarkets);
		}
		
	}

	private void callRulesEngine(Jogo jogo, List<Precos> precosList, int[] marketId, InputValuesService ivs) {
		
		ivs.setBetRule(new ApostaRule());
		ivs.getBetRule().setJogo(Utils.buildGameVs(jogo,false));
		ivs.getBetRule().setIdMarket(marketId[0]);
		ivs.getBetRule().setMinutos(jogo.getData().getTime());
		
		for(Precos price : precosList){
			try{
				EngineStart st = new EngineStart();
				st.Start(ivs.loadAposta(price.getIdPrecos(),jogo.getIdJogo()), price);
				// and then dispose the session
				st.getKsession().dispose();
			}catch(Exception e){
				System.out.println("Excepcao:"+e.getMessage());
			}
		}
		
		
		
	}

	private List<RunnerContract> getRunners(InflatedMarketPrices prices) {

		List<RunnerContract> runners = new ArrayList<RunnerContract>();
		
		for (InflatedRunner r : prices.getRunners()) {

			Double lay = 0.0;
			if (r.getLayPrices().size() > 0) {
				InflatedPrice p = r.getLayPrices().get(0);
				lay = p.getPrice();
			}

			Double back = 0.0;
			if (r.getBackPrices().size() > 0) {
				InflatedPrice p = r.getBackPrices().get(0);
				back = p.getPrice();
			}
			
			runners.add(new RunnerContract(back, lay, r.getSelectionId()));
		}
		
		return runners;
	}

	
	public BFEvent[] getEvents(GetEventsResp resp,Integer eventId){
		
		try {
			resp = GlobalAPI.getEvents(apiContext, eventId);
			BFEvent[] events = resp.getEventItems().getBFEvent();
			return events;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}
	
	
	public BFEvent[] getEvents(GetEventsResp resp, BFEvent[] events, String str){

		try {
			BFEvent[] eventsList=null;

			if(events!=null){

				for(BFEvent event : events){
					if(event.getEventName().contains(str)){
						//prblema ao utilizar duas threads
						resp = GlobalAPI.getEvents(apiContext, event.getEventId());

						eventsList = resp.getEventItems().getBFEvent();

					}
				}
			
			}
			
			return eventsList;
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;

	}
	
	public MarketSummary[] getMarkets(GetEventsResp resp,BFEvent[] gamesEvents,String gameStr, Jogo game, int[] eventId){
		
		try {
		
			String originalVs = gameStr;
			
			if(gamesEvents!=null){
				
				boolean findGameName = false;
				Integer numTries = 0;
				
				for(int i=0;i<gamesEvents.length;i++){
					
					if(gamesEvents[i].getEventName().matches(gameStr)){
						
						findGameName = true;
						
						resp = GlobalAPI.getEvents(apiContext, gamesEvents[i].getEventId());
						
            eventId[0] = gamesEvents[i].getEventId();
                  
						return resp.getMarketItems().getMarketSummary();
						
					}
					
					if(i==gamesEvents.length-1 && findGameName == false){
						
						if(numTries<2){
							gameStr = Utils.buildGameRegex(game,numTries);
							i=0;
							numTries++;
						}else{
							EmailService eS = new EmailService();
							eS.enviaEmail("Jogo nao encontrado","Jogo nao encontrado",originalVs);
							return null;
						}
						
					}
				}
			}
					
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;

	}
	
    public MarketSummary[] getMarkets(GetEventsResp resp, int[] eventId){
		
		try {
	  
						resp = GlobalAPI.getEvents(apiContext, eventId[0]);
 
                  
						return resp.getMarketItems().getMarketSummary();
 
 
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;

	} 
   
	
	public InflatedMarketPrices getPrices(int[] marketId, MarketSummary[] markets,String str, Semaphore semaphore2){
		
		InflatedMarketPrices prices = null;
		
		if(markets!=null){
			
			for(MarketSummary market : markets){

				if(market.getMarketName().contentEquals(str)){
	
					try {
						prices = ExchangeAPI.getMarketPrices(Exchange.UK, apiContext, market.getMarketId(),semaphore);
						marketId[0] = market.getMarketId();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		
		if(prices==null){
			prices = new InflatedMarketPrices();
		}
		return prices;
			
	}
	
	public void efectuaLogin() {
		
		String username = (String) Prop.properties.get("soccerBet.betfair.username");
		String password = (String) Prop.properties.get("soccerBet.betfair.password");
		
		try
		{
			GlobalAPI.login(apiContext, username, password);
		}
		catch (Exception e)
		{
			System.out.println("Não foi possivel efectuar o login");
			System.out.println("Exce: "+e.getMessage());
		}
		
	}
	
	public void efectuaLogout() {
		try
		{
			GlobalAPI.logout(apiContext);
		}
		catch (Exception e)
		{
			System.out.println("Não foi possivel efectuar o logout");
			System.out.println("Exce: "+e.getMessage());
		}
		
	}
	

	public void placeBet(Precos preco,ApostaRule rule){
		try {			
			
			BetService betService = new BetService(preco,rule);
			
			if(Boolean.parseBoolean((String) Prop.properties.get("soccerBet.bet"))==true && Funds.avalableFunds > 1){
				// We can ignore the array here as we only sent in one bet.
				PlaceBetsResult betResult;
				
				betResult = ExchangeAPI.placeBets(Exchange.UK, apiContext, new PlaceBets[] {betService.getBet()})[0];
				
				if (betResult.getSuccess()) {
					System.out.println("Bet "+betResult.getBetId()+" placed. "+betResult.getSizeMatched() +" matched @ "+betResult.getAveragePriceMatched());
					betService.processaAposta(preco,rule);
				} else {
					betService.processaErro(preco,betResult.getResultCode());
				}
			}else
				if(Boolean.parseBoolean((String) Prop.properties.get("soccerBet.bet"))==false){
					betService.processaAposta(preco,rule);
				}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
